package com.sc.nft.controller;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.sc.nft.annotation.UserLogin;
import com.sc.nft.controller.request.*;
import com.sc.nft.controller.vo.CollectiveOrderPageVO;
import com.sc.nft.controller.vo.CollectiveOrderVO;
import com.sc.nft.controller.vo.MergeProcessVO;
import com.sc.nft.controller.vo.MyIntoVO;
import com.sc.nft.dao.*;
import com.sc.nft.dto.CollectiveIntoVO;
import com.sc.nft.dto.CollectiveSalesDetailVO;
import com.sc.nft.dto.CollectiveSalesPageVO;
import com.sc.nft.entity.*;
import com.sc.nft.entity.vo.CollectiveTradingTrendVO;
import com.sc.nft.entity.vo.LockOrderVO;
import com.sc.nft.entity.vo.SecondaryMarketSearchVO;
import com.sc.nft.enums.OrderStatusEnum;
import com.sc.nft.enums.PayTypeEnum;
import com.sc.nft.enums.RedisKeyEnum;
import com.sc.nft.enums.SecondPayOrderItemTypeEnum;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.mapper.CollectiveOrderMapper;
import com.sc.nft.mapper.CollectiveSalesConfigMapper;
import com.sc.nft.pool.UserPool;
import com.sc.nft.service.CollectiveOrderService;
import com.sc.nft.service.CollectiveSalesConfigService;
import com.sc.nft.service.CollectiveSalesService;
import com.sc.nft.sup.ErrorCode;
import com.sc.nft.sup.PageRequest;
import com.sc.nft.sup.PageResult;
import com.sc.nft.sup.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/collective")
@Api(tags = "集售模块")
public class CollectiveSalesController {

    private final CollectiveSalesConfigService collectiveSalesConfigService;
    private final CollectiveSalesConfigMapper collectiveSalesConfigMapper;
    private final CollectiveSalesConfigDao collectiveSalesConfigDao;
    private final CollectiveSalesService collectiveSalesService;
    private final CollectiveOrderService collectiveOrderService;
    private final CollectiveOrderMapper collectiveOrderMapper;
    private final CollectiveSalesTimePointsDao collectiveSalesTimePointsDao;
    private final CollectiveSalesDao collectiveSalesDao;
    private final Redisson redisson;
    private final FilterIncomeListDao filterIncomeListDao;
    private final DigitalCollectionDao digitalCollectionDao;
    private final CollectiveOrderDao collectiveOrderDao;
    private final CatenaInfoDao catenaInfoDao;
    private final UserInfoDao userInfoDao;
    private final RedisTemplate redisTemplate;
    private final PayOrderDao payOrderDao;
    private final PayOrderItemDao payOrderItemDao;


    @ApiOperation(value = "集售市场列表", notes = "集售市场列表", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/page", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public PageResult<CollectiveSalesPageVO> page(@RequestBody @Valid PageRequest request) {
        return PageResult.success(collectiveSalesConfigService.showCustomerPage(request.toMp()));
    }

    @ApiOperation(value = "集售市场搜索", notes = "集售市场搜索", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/search", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public PageResult<SecondaryMarketSearchVO> page(@RequestBody @Valid FirstTitlePageRequest request) {
        return PageResult.success(collectiveSalesConfigMapper.searchWithFirstTitle(request.toMp(), request.getFirstTitle()));
    }


    @ApiOperation(value = "集售详情", notes = "集售详情", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/detail", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result<CollectiveSalesDetailVO> detail(@RequestBody @Valid IdRequest request) {
        return Result.success(collectiveSalesConfigService.getDetail(request.getId(), UserPool.getUser()));
    }

    @ApiOperation(value = "获取投入信息", notes = "获取投入信息", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/getIntoInfo", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result<CollectiveIntoVO> getIntoInfo(@RequestBody @Valid IdRequest request) {
        return Result.success(collectiveSalesConfigService.getIntoInfo(request.getId(), UserPool.getUser()));
    }


    @ApiOperation(value = "成交趋势", notes = "成交趋势", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/tradingTrend", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public PageResult<CollectiveTradingTrendVO> tradingTrend(@RequestBody @Valid IdPageRequest request) {
        return PageResult.success(collectiveOrderMapper.tradingTrend(request.toMp(), request.getId()));
    }

    @ApiOperation(value = "订单详情", notes = "订单详情", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/orderDetail", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result<CollectiveOrderVO> orderDetail(@RequestBody @Valid IdRequest request) {
        CollectiveOrder collectiveOrder = collectiveOrderDao.getById(request.getId());
        CollectiveOrderVO collectiveOrderVO = new CollectiveOrderVO();
        //TODO 查询集售支付单6.3.6-pay 支付改造 此处可能存在orderId与普通藏品订单重合的情况 连表查询带上集售类型查询
        if(OrderStatusEnum.PAYMENT.equals(collectiveOrder.getStatus())){
            PayOrder payOrder = payOrderItemDao.getByOrderIdAndType(collectiveOrder.getId(),
                    SecondPayOrderItemTypeEnum.COLLECTION, PayTypeEnum.COLLECTIVE);
            collectiveOrderVO.setPayType(payOrder.getPayType());
            collectiveOrderVO.setPayIcon(null != payOrder.getPayType() ? payOrder.getPayType().getIcon() : null);
        }
        collectiveOrderVO.setReceiveType(collectiveOrder.getReceiveType());
        collectiveOrderVO.setReceiveIcon(null!=collectiveOrder.getReceiveType()?collectiveOrder.getReceiveType().getIcon():null);
        collectiveOrderVO.setId(collectiveOrder.getId());
        collectiveOrderVO.setCollectionId(collectiveOrder.getCollectionId());
        collectiveOrderVO.setCollectionName(collectiveOrder.getCollectionName());
        collectiveOrderVO.setCreationContent(collectiveOrder.getCreationContent());
        collectiveOrderVO.setCharge(collectiveOrder.getCharge().toPlainString());
        collectiveOrderVO.setGas(collectiveOrder.getGas().toPlainString());
        collectiveOrderVO.setBuyPrice(collectiveOrder.getBuyPrice().toPlainString());
        collectiveOrderVO.setNumbering(collectiveOrder.getNumbering());
        if (collectiveOrder.getStatus() != OrderStatusEnum.PAYMENT) {
            collectiveOrderVO.setCloseTime(collectiveOrder.getCloseTime());
        }
        collectiveOrderVO.setOrderTime(collectiveOrder.getOrderTime());
        collectiveOrderVO.setOrderNo(collectiveOrder.getOrderNo());
        collectiveOrderVO.setRealReceiveAmount(collectiveOrder.getRealReceiveAmount().toPlainString());
        collectiveOrderVO.setPayTime(collectiveOrder.getPayTime());
        collectiveOrderVO.setStatus(collectiveOrder.getStatus());
        DigitalCollection collection = digitalCollectionDao.getById(collectiveOrder.getCollectionId());
        collectiveOrderVO.setLevel(catenaInfoDao.getCatenaNameById(collection.getCatenaId()));
        collectiveOrderVO.setBuyUserNickName(userInfoDao.getNickNameById(collectiveOrder.getBuyUserId()));
        collectiveOrderVO.setSalesUserNickName(userInfoDao.getNickNameById(collectiveOrder.getSalesUserId()));
        if (collectiveOrder.getStatus() == OrderStatusEnum.WAIT_PAY) {
            collectiveOrderVO.setCountDownTime(Math.max(collectiveOrder.getCloseTime().getTime() - DateTime.now().getTime(), 0L));
        }
        return Result.success(collectiveOrderVO);
    }


    @ApiOperation(value = "投入藏品", notes = "投入藏品", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/into", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result into(@RequestBody @Valid CollectiveIntoRequest request) {
        if (!StrUtil.equals(request.getDealPwd(), UserPool.getUser().getTransactionPassword())) {
            return Result.fail(ErrorCode.PAYMENT_PASSWORD_WAS_ENTERED_INCORRECTLY);
        }
        FilterIncomeList filterIncomeList = filterIncomeListDao.getIsTeamIncomeByUserId(UserPool.getUser().getId());
        if (ObjectUtil.isNotNull(filterIncomeList)) {
            if (filterIncomeList.getDaoId().longValue() == 6L || filterIncomeList.getDaoId().longValue() == 7L) {

            } else {
                return Result.fail("该账号禁止参与");
            }
        }
        RLock lock = redisson.getLock(RedisKeyEnum.COLLECTIVE_INTO.getKey() + request.getId());
        boolean b = false;
        try {
            b = lock.tryLock(10, TimeUnit.SECONDS);
            if (b) {
                collectiveSalesService.into(request.getId(), request.getCount(), UserPool.getUser());
                return Result.success();
            } else {
                return Result.fail(ErrorCode.LATER_TRY);
            }
        } catch (InterruptedException e) {
            return Result.fail(ErrorCode.LATER_TRY);
        } finally {
            lock.unlock();
        }
    }

    @ApiOperation(value = "我的投入(传入藏品Id)", notes = "我的投入", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/myInto", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result<MyIntoVO> myInto(@RequestBody @Valid IdRequest request) {
        DigitalCollection collection = digitalCollectionDao.getById(request.getId());
        MyIntoVO myIntoVO = new MyIntoVO();
        myIntoVO.setFirstTitle(collection.getFirstTitle());
        myIntoVO.setCreationContent(collection.getCreationContent());
        myIntoVO.setIntoCount(collectiveSalesDao.getUserCollectionSumCount(UserPool.getUser().getId(), request.getId()));
        myIntoVO.setRevokeCount(collectiveSalesDao.getUserCollectionCanRevokeCount(UserPool.getUser().getId(), request.getId()));
        myIntoVO.setDealCount(collectiveSalesDao.getUserCollectionDealCount(UserPool.getUser().getId(), request.getId()));
        return Result.success(myIntoVO);
    }


    @ApiOperation(value = "撤销投入(传入藏品Id)", notes = "我的投入", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/revoke", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result revoke(@RequestBody @Valid CollectiveIntoRequest request) {
        if (!StrUtil.equals(request.getDealPwd(), UserPool.getUser().getTransactionPassword())) {
            return Result.fail(ErrorCode.PAYMENT_PASSWORD_WAS_ENTERED_INCORRECTLY);
        }
        collectiveSalesService.revoke(request.getId(), request.getCount(), UserPool.getUser());
        return Result.success(request.getCount());
    }

    @ApiOperation(value = "单个撤销(传用户藏品Id)", notes = "单个撤销", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/revokeOne", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result revoke(@RequestBody @Valid DealPwdRequest request) {
        if (!StrUtil.equals(request.getDealPwd(), UserPool.getUser().getTransactionPassword())) {
            return Result.fail(ErrorCode.PAYMENT_PASSWORD_WAS_ENTERED_INCORRECTLY);
        }
        collectiveSalesService.revokeOne(request.getId(), UserPool.getUser());
        return Result.success();
    }

    @ApiOperation(value = "集售订单列表", notes = "集售订单列表", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/orderPage", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public PageResult<CollectiveOrderPageVO> revoke(@RequestBody @Valid OrderStatusPageRequest request) {
        Page<CollectiveOrder> orderPage = collectiveOrderMapper.getOrderPage(request.toMp(), UserPool.getUser().getId(), request.getOrderType(), request.getYearMonth() != null ? DateUtil.beginOfMonth(request.getYearMonth()) : null, request.getYearMonth() != null ? DateUtil.endOfMonth(request.getYearMonth()) : null);
        Page<CollectiveOrderPageVO> result = new Page<>(orderPage.getCurrent(), orderPage.getSize(), orderPage.getTotal());
        List<CollectiveOrderPageVO> list = Lists.newArrayList();
        for (CollectiveOrder record : orderPage.getRecords()) {
            CollectiveOrderPageVO collectiveOrderPageVO = new CollectiveOrderPageVO();
            collectiveOrderPageVO.setId(record.getId());
            collectiveOrderPageVO.setCollectiveId(collectiveSalesConfigDao.getByCollection(record.getCollectionId()).getId());
            collectiveOrderPageVO.setOrderNo(record.getOrderNo());
            collectiveOrderPageVO.setStatus(record.getStatus());
            collectiveOrderPageVO.setNumbering(record.getNumbering());
            collectiveOrderPageVO.setBuyPrice(record.getBuyPrice().toPlainString());
            collectiveOrderPageVO.setCollectionName(record.getCollectionName());
            collectiveOrderPageVO.setCreationContent(record.getCreationContent());
            collectiveOrderPageVO.setOrderTime(record.getOrderTime());
            switch (collectiveOrderPageVO.getStatus()) {
                case CLOSE:
                    collectiveOrderPageVO.setCancelTime(record.getCloseTime());
                    break;
                case PAYMENT:
                    collectiveOrderPageVO.setPayTime(record.getPayTime());
                    break;
            }
            list.add(collectiveOrderPageVO);
        }
        result.setRecords(list);
        return PageResult.success(result);
    }

    @ApiOperation(value = "取消待付款订单", notes = "取消待付款订单", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/closeOrder", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result revoke(@RequestBody @Valid CloseOrdersRequest request) {

        collectiveOrderService.cancelOrder(UserPool.getUser(), request.getIds());
        String lockKey = RedisKeyEnum.COLLECTIVE_USER_LOCK.getKey() + UserPool.getUser().getId();
        redisTemplate.opsForValue().increment(lockKey, request.getIds().size());
        redisTemplate.expire(lockKey, 6, TimeUnit.HOURS);
        return Result.success();
    }


    @ApiOperation(value = "合并处理列表", notes = "合并处理列表", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/mergeProcessing", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public PageResult<MergeProcessVO> mergeProcessing(@RequestBody @Valid PageRequest request) {
        Page<CollectiveOrder> userWaitPayOrderPage = collectiveOrderDao.getUserWaitPayOrderPage(request.toMp(), UserPool.getUser().getId());
        Page<MergeProcessVO> page = new Page<>(userWaitPayOrderPage.getCurrent(), userWaitPayOrderPage.getSize(), userWaitPayOrderPage.getTotal());
        List<MergeProcessVO> vos = Lists.newArrayList();
        for (CollectiveOrder record : userWaitPayOrderPage.getRecords()) {
            MergeProcessVO mergeProcessVO = new MergeProcessVO();
            mergeProcessVO.setId(record.getId());
            mergeProcessVO.setCollectiveId(collectiveSalesConfigDao.getByCollection(record.getCollectionId()).getId());
            mergeProcessVO.setCollectionId(record.getCollectionId());
            mergeProcessVO.setCollectionName(record.getCollectionName());
            mergeProcessVO.setNumbering(record.getNumbering());
            mergeProcessVO.setCreationContent(record.getCreationContent());
            mergeProcessVO.setBuyPrice(record.getBuyPrice().toPlainString());
            mergeProcessVO.setSumCount(digitalCollectionDao.getById(record.getCollectionId()).getSumCount());
            vos.add(mergeProcessVO);
        }
        page.setRecords(vos);
        return PageResult.success(page);
    }

    @ApiOperation(value = "计算购买价格", notes = "计算购买价格", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/calBuyPrice", produces = MediaType.APPLICATION_JSON_VALUE)
    public Result<String> revoke(@RequestBody @Valid CountRequest request) {
        CollectiveSalesTimePoints nowPrice = collectiveSalesTimePointsDao.getNowPrice(request.getId());
        BigDecimal price = null;
        if (nowPrice != null) {
            price = nowPrice.getPrice();
        } else {
            CollectiveSalesConfig collectiveSalesConfig = collectiveSalesConfigDao.getById(request.getId());
            price = collectiveSalesConfig.getStartBuyPrice();
        }
        return Result.success(price.multiply(BigDecimal.valueOf(request.getCount())).toPlainString());
    }


    @ApiOperation(value = "下单", notes = "下单", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/createOrder", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result<LockOrderVO> createOrder(@RequestBody @Valid CountRequest request) {
        if (request.getCount() > 10) {
            return Result.fail("单次下单数量不能超过10单");
        }

        String lockKey = RedisKeyEnum.COLLECTIVE_USER_LOCK.getKey() + UserPool.getUser().getId();
        FilterIncomeList filterIncomeList = filterIncomeListDao.getByUserId(UserPool.getUser().getId());
        if (ObjectUtil.isNull(filterIncomeList) && BooleanUtil.isFalse(UserPool.getUser().getIsSm())) {
            return Result.fail(ErrorCode.PLEASE_PERFORM_REAL_NAME_AUTHENTICATION_FIRST);
        } else {
            Boolean b = redisTemplate.hasKey(lockKey);
            if (b) {
                Integer lockSum = Convert.toInt(redisTemplate.opsForValue().get(lockKey));
                if (lockSum >= 3) {
                    throw new GlobalRunTimeException(ErrorCode.ARE_MORE_THAN_3_UNPAID_ORDERS_ON_THE_DAY_WHICH_WILL_BE_AUTOMATICALLY_CANCELLED);
                }
            }
        }
        RLock lock = redisson.getLock(RedisKeyEnum.COLLECTIVE_ORDER.getKey() + request.getId());
        boolean b = false;
        try {
            b = lock.tryLock(10, TimeUnit.SECONDS);
            if (b) {
                return Result.success(collectiveOrderService.createOrder(UserPool.getUser(), request.getId(), request.getCount()));
            } else {
                return Result.fail(ErrorCode.LATER_TRY);
            }
        } catch (InterruptedException e) {
            return Result.fail(ErrorCode.LATER_TRY);
        } finally {
            lock.unlock();
        }
    }


    @ApiOperation(value = "合并支付", notes = "合并支付", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/mergePay", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result<LockOrderVO> mergePay(@RequestBody @Valid IdsRequest request) {
        List<CollectiveOrder> userWaitPayOrderBuyIds = collectiveOrderDao.getUserWaitPayOrderBuyIds(UserPool.getUser().getId(), request.getIds());
        Assert.isTrue(userWaitPayOrderBuyIds.size() == request.getIds().size(), () -> new GlobalRunTimeException("有订单状态发生改变，请刷新后再试"));
        LockOrderVO lockOrderVO = new LockOrderVO();
        lockOrderVO.setOrderIds(userWaitPayOrderBuyIds.stream().map(CollectiveOrder::getId).collect(Collectors.toList()));
        lockOrderVO.setNowDate(DateTime.now());
        lockOrderVO.setExpirationTime(userWaitPayOrderBuyIds.stream().map(CollectiveOrder::getCloseTime).min(Comparator.naturalOrder()).get());
        lockOrderVO.setMoney(userWaitPayOrderBuyIds.stream().map(CollectiveOrder::getBuyPrice).reduce(BigDecimal::add).get());
        lockOrderVO.setSumCount(userWaitPayOrderBuyIds.size());
        return Result.success(lockOrderVO);
    }


}
